package chooongg.base.utils

import java.io.*
import java.security.DigestInputStream
import java.security.MessageDigest
import java.security.NoSuchAlgorithmException
import java.util.*
import kotlin.experimental.and

fun File?.exists() = this != null && this.exists()

fun File?.notExists() = this == null || !this.exists()

/**
 * 字符串路径2File
 */
fun String.getFileByPath() = if (isSpace()) null else File(this)


/**
 * 文件是否存在
 */
fun String.isFileExists() = if (isSpace()) false else File(this).exists()

/**
 * 重命名文件
 *
 * @param newName 新文件名
 */
fun File.renameFile(newName: String): Boolean {
    if (!this.exists() || newName.isSpace()) return false
    if (newName == this.name) return true
    val newFile = File(this.parent!! + File.separator + newName)
    return !newFile.exists() && this.renameTo(newFile)
}

/**
 * 是否是目录
 */
fun File.isFileDir() = this.exists() && this.isDirectory

/**
 * 创建目录，如果不存在则创建
 */
fun File.createOrExistsDir() = if (this.exists()) this.isDirectory else this.mkdirs()

/**
 * 创建文件，如果不存在则创建
 */
fun File.createOrExistsFile(): Boolean {
    if (exists()) return isFile
    if (!createOrExistsDir()) return false
    return try {
        createNewFile()
    } catch (e: IOException) {
        false
    }
}

/**
 * 创建文件，如果存在则删除旧文件
 */
fun File.createFileByDeleteOldFile(): Boolean {
    if (exists() && !delete()) return false
    if (!createOrExistsDir()) return false
    return try {
        createNewFile()
    } catch (e: IOException) {
        false
    }
}


/**
 * 复制目录
 *
 * @param destDir 目标目录
 */
fun File.copyDir(destDir: File?) = copyOrMoveDir(this, destDir, false)

/**
 * 复制目录
 *
 * @param destDir 目标目录
 * @param listener 监听器
 */
fun File.copyDir(destDir: File?, listener: OnReplaceListener) =
    copyOrMoveDir(this, destDir, listener, false)

/**
 * 复制文件
 *
 * @param destFile 目标文件
 */
fun File.copyFile(destFile: File?) = copyOrMoveFile(this, destFile, false)

/**
 * 复制文件
 *
 * @param destFile 目标文件
 * @param listener 监听器
 */
fun File.copyFile(destFile: File?, listener: OnReplaceListener) =
    copyOrMoveFile(this, destFile, listener, false)

/**
 * 移动目录
 *
 * @param destDir 目标目录
 */
fun File.moveDir(destDir: File?) = copyOrMoveDir(this, destDir, true)

/**
 * 移动目录
 *
 * @param destDir 目标目录
 * @param listener 监听器
 */
fun File.moveDir(destDir: File?, listener: OnReplaceListener) =
    copyOrMoveDir(this, destDir, listener, true)

/**
 * 移动文件
 *
 * @param destFile 目标文件
 */
fun File.moveFile(destFile: File?) = copyOrMoveFile(this, destFile, true)

/**
 * 移动文件
 *
 * @param destFile 目标文件
 * @param listener 监听器
 */
fun File.moveFile(destFile: File?, listener: OnReplaceListener) =
    copyOrMoveFile(this, destFile, listener, true)

private fun copyOrMoveDir(srcDir: File?, destDir: File?, isMove: Boolean): Boolean {
    return copyOrMoveDir(srcDir, destDir, object : OnReplaceListener {
        override fun onReplace(): Boolean {
            return true
        }
    }, isMove)
}

private fun copyOrMoveDir(
    srcDir: File?,
    destDir: File?,
    listener: OnReplaceListener?,
    isMove: Boolean
): Boolean {
    if (srcDir == null || destDir == null) return false
    val srcPath = srcDir.path + File.separator
    val destPath = destDir.path + File.separator
    if (destPath.contains(srcPath)) return false
    if (!srcDir.exists() || !srcDir.isDirectory) return false
    if (destDir.exists()) if (listener == null || listener.onReplace()) {
        if (!destDir.deleteAllInDir()) return false
    } else {
        return true
    }
    if (!destDir.createOrExistsDir()) return false
    val files = srcDir.listFiles()
    for (file in files!!) {
        val oneDestFile = File(destPath + file.name)
        if (file.isFile) {
            if (!copyOrMoveFile(file, oneDestFile, listener, isMove)) return false
        } else if (file.isDirectory) {
            if (!copyOrMoveDir(file, oneDestFile, listener, isMove)) return false
        }
    }
    return !isMove || srcDir.deleteDir()
}

private fun copyOrMoveFile(srcFile: File?, destFile: File?, isMove: Boolean): Boolean {
    return copyOrMoveFile(srcFile, destFile, object : OnReplaceListener {
        override fun onReplace(): Boolean {
            return true
        }
    }, isMove)
}

private fun copyOrMoveFile(
    srcFile: File?,
    destFile: File?,
    listener: OnReplaceListener?,
    isMove: Boolean
): Boolean {
    if (srcFile == null || destFile == null) return false
    if (srcFile == destFile) return false
    if (!srcFile.exists() || !srcFile.isFile) return false
    if (destFile.exists()) if (listener == null || listener.onReplace()) {
        if (!destFile.delete()) {
            return false
        }
    } else {
        return true
    }
    if (destFile.parentFile?.createOrExistsDir() == false) return false
    return try {
        srcFile.writeFileFromIS(FileInputStream(srcFile)) && !(isMove && !srcFile.delete())
    } catch (e: FileNotFoundException) {
        e.printStackTrace()
        false
    }

}

/**
 * 删除目录
 */
fun File.deleteDir(): Boolean {
    if (!exists()) return true
    if (!isDirectory) return false
    val files = this.listFiles()
    if (files != null && files.isNotEmpty()) {
        for (file in files) {
            when {
                file.isFile -> if (!file.delete()) return false
                file.isDirectory -> if (!file.deleteDir()) return false
            }
        }
    }
    return this.delete()
}

/**
 * 删除目录下所有东西
 */
fun File.deleteAllInDir() = deleteFilesInDirWithFilter(FileFilter { true })

/**
 * 删除目录下所有文件
 */
fun File.deleteFilesInDir() = deleteFilesInDirWithFilter(FileFilter { pathname -> pathname.isFile })

/**
 * 删除目录下所有过滤的文件
 *
 * @param filter 过滤器
 */
fun File.deleteFilesInDirWithFilter(filter: FileFilter): Boolean {
    if (!exists()) return true
    if (!isDirectory) return false
    val files = listFiles()
    if (files != null && files.isNotEmpty()) {
        for (file in files) {
            if (filter.accept(file)) {
                when {
                    file.isFile -> if (!file.delete()) return false
                    file.isDirectory -> if (!file.deleteDir()) return false
                }
            }
        }
    }
    return true
}

/**
 * 获取目录下的文件列表
 *
 * @param isRecursive 是否遍历子目录
 */
@JvmOverloads
fun File.getFilesInDir(isRecursive: Boolean = false) =
    listFilesInDirWithFilter(FileFilter { true }, isRecursive)


/**
 * 获取目录下的文件列表带过滤器
 *
 * @param filter      过滤器
 * @param isRecursive 是否遍历子目录
 */
@JvmOverloads
fun File.listFilesInDirWithFilter(filter: FileFilter, isRecursive: Boolean = false): List<File>? {
    if (!isFileDir()) return null
    val list = ArrayList<File>()
    val files = listFiles()
    if (files != null && files.isNotEmpty()) {
        for (file in files) {
            if (filter.accept(file)) {
                list.add(file)
            }
            if (isRecursive && file.isDirectory) {
                list.addAll(listFilesInDirWithFilter(filter, true) ?: emptyList())
            }
        }
    }
    return list
}

/**
 * 简单的获取文件字符集
 */
fun File.getFileCharsetSimple(): String {
    var p = 0
    var `is`: InputStream? = null
    try {
        `is` = BufferedInputStream(FileInputStream(this))
        p = (`is`.read() shl 8) + `is`.read()
    } catch (e: IOException) {
        e.printStackTrace()
    } finally {
        try {
            `is`?.close()
        } catch (e: IOException) {
            e.printStackTrace()
        }
    }
    return when (p) {
        0xefbb -> "UTF-8"
        0xfffe -> "Unicode"
        0xfeff -> "UTF-16BE"
        else -> "GBK"
    }
}

/**
 * 返回文件的行数
 *
 * @param file The file.
 * @return the number of lines of file
 */
@Suppress("RECEIVER_NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS")
fun File.getFileLines(): Int {
    var count = 1
    var `is`: InputStream? = null
    try {
        `is` = BufferedInputStream(FileInputStream(this))
        val buffer = ByteArray(1024)
        var readChars: Int
        if (System.getProperty("line.separator")!!.endsWith("\n")) {
            while (`is`.read(buffer, 0, 1024).apply { readChars = this } != -1) {
                for (i in 0 until readChars) {
                    if (buffer[i] == '\n'.toByte()) ++count
                }
            }
        } else {
            while (`is`.read(buffer, 0, 1024).apply { readChars = this } != -1) {
                for (i in 0 until readChars) {
                    if (buffer[i] == '\r'.toByte()) ++count
                }
            }
        }
    } catch (e: IOException) {
        e.printStackTrace()
    } finally {
        try {
            `is`?.close()
        } catch (e: IOException) {
            e.printStackTrace()
        }
    }
    return count
}

/**
 * 获取目录大小
 */
fun File.getDirSize() = if (getDirLength() == -1L) "" else byte2FitMemorySize(getDirLength())


/**
 * 获取文件大小
 */
fun File.getFileSize() = if (length() == -1L) "" else byte2FitMemorySize(length())


/**
 * 获取目录长度
 */
fun File.getDirLength(): Long {
    if (!isFileDir()) return -1
    var len: Long = 0
    val files = listFiles()
    if (files != null && files.isNotEmpty()) {
        for (file in files) {
            len += if (file.isDirectory) {
                getDirLength()
            } else {
                file.length()
            }
        }
    }
    return len
}

/**
 * 获取文件的 MD5 校验码
 */
fun File.getFileMD5ToString() = bytes2HexString(getFileMD5())


/**
 * 获取文件的 MD5 校验码
 */
fun File.getFileMD5(): ByteArray? {
    var dis: DigestInputStream? = null
    try {
        val fis = FileInputStream(this)
        var md = MessageDigest.getInstance("MD5")
        dis = DigestInputStream(fis, md)
        val buffer = ByteArray(1024 * 256)
        while (true) {
            if (dis.read(buffer) <= 0) break
        }
        md = dis.messageDigest
        return md.digest()
    } catch (e: NoSuchAlgorithmException) {
        e.printStackTrace()
    } catch (e: IOException) {
        e.printStackTrace()
    } finally {
        try {
            dis?.close()
        } catch (e: IOException) {
            e.printStackTrace()
        }
    }
    return null
}

/**
 * 根据文件名判断文件是否为图片
 */
fun File.isImage() = this.path.isImageFile()


/**
 * 根据文件名判断文件是否为图片
 */
fun String.isImageFile(): Boolean {
    val path = this.toUpperCase()
    return (path.endsWith(".PNG") || path.endsWith(".JPG")
            || path.endsWith(".JPEG") || path.endsWith(".BMP")
            || path.endsWith(".GIF") || path.endsWith(".WEBP"))
}

///////////////////////////////////////////////////////////////////////////
// copy from ConvertUtils
///////////////////////////////////////////////////////////////////////////

private val HEX_DIGITS =
    charArrayOf('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F')

private fun bytes2HexString(bytes: ByteArray?): String {
    if (bytes == null) return ""
    val len = bytes.size
    if (len <= 0) return ""
    val ret = CharArray(len shl 1)
    var i = 0
    var j = 0
    while (i < len) {
        ret[j++] = HEX_DIGITS[bytes[i].toInt() ushr 4 and 0x0f]
        ret[j++] = HEX_DIGITS[(bytes[i] and 0x0f).toInt()]
        i++
    }
    return String(ret)
}

interface OnReplaceListener {
    fun onReplace(): Boolean
}